Skip to content

🧵Refactor receive responses thread sync#693

Closed
nevans wants to merge 8 commits into
masterfrom
refactor-receive-responses-thread-sync
Closed

🧵Refactor receive responses thread sync#693
nevans wants to merge 8 commits into
masterfrom
refactor-receive-responses-thread-sync

Conversation

@nevans

@nevans nevans commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

Related to #692, but that handles code that executes in client thread(s) and this handles code that executes in the receiver thread.

Notable changes:

  • The receiver loop only calls synchronize once per loop, to handle the response.
    • Various exception handlers and cleanup code is moved into adjacent synchronize blocks.
    • Resetting @exception at the start is moved to the end (unless connection_closed).
  • The logout state is now entered before signaling the conditional variables for the last time. This is less surprising than doing it afterward, and it also makes some of the other state_logout! calls redundant. connection_state is relatively new, and it is not expected that this difference should impact much code anyway.
  • An EOFError may be assigned a receiver thread backtrace, similarly to other exceptions that are raised by response reading or parsing. This will be significantly improved by 🧵🥅 Reraise receiver thread errors with caller's backtrace #691.
  • Simplified exception handling by simply letting the response reader and response parser exceptions bubble up, which allows us to assign @exception in the synchronize block inside the ensure clause. This makes the loop much simpler to understand, and now we don't release and immediately re-acquire the lock with (slightly) inconsistent state.
  • @sock.close for exceptions from get_response is no longer synchronized. This converts some instances of IOError, "stream closed" into IOError, "stream closed by another thread".
    This change was reverted. It made the tests flakier, and it's not too burdensome to keep it in a synchronized block.

@nevans

nevans commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator Author

It looks like some of this made tests that assert specific connection closing errors a bit flakier. On the one hand, it's not a big deal if client and receiver thread code, running simultaneously, sometimes results in IOError, "closed in another thread" rather than some specific error. On the other hand... maybe we should try to avoid that inconsistency, if only for keeping the test suite simple.

@nevans nevans force-pushed the refactor-receive-responses-thread-sync branch 2 times, most recently from db93f56 to cddcbc9 Compare June 7, 2026 19:35
nevans added 6 commits June 7, 2026 16:41
I'm not sure why this was outside the `synchronize`...
There's no good reason to release the lock only to immediately reacquire
it for exception handling.  That might allow commands to be started (or
continued) while still propagating irrelevant response errors.

By moving the `rescue` clause inside the `synchronize` block, `begin`
and `end` are now superfluous and can be removed.  But that makes the
diff seem much larger than it should, so I'm saving it for a later
branch.
Although it wasn't obvious at first glance, because nearly every
possible error is caught by a `rescue Exception` clause, this is
effectively no different from what was already happening.  This
structure makes that much more obvious.

Also, by moving this code into the `ensure`, it allows us to simplify
error handling elsewhere, by simply raising or re-raising an exception.

This also lets us move `state_logout!` inside the `synchronize` block.
By resetting `@exception` inside the synchronize block at the end of the
loop, we don't need to release and immediately reacquire the lock each
time through the loop.
The receiver thread now enters the logout state _before_ signaling the
conditional variables for the last time.  This is less surprising than
doing it afterward, and it also makes some of the other `state_logout!`
calls redundant.
With this change, an EOFError `@exception` may be assigned a receiver
thread backtrace.  Previously, it might've been raised in the client
thread which, would set the backtrace for that client thread.
nevans added 2 commits June 8, 2026 09:53
This delays setting `@exception` until it can be synchronized with
everything else in the `ensure` block.

NOTE: this could be simplified further: drop the `rescue` clause and
use `$!` in the `ensure` clause.  But that seems to trigger a
TruffleRuby bug: truffleruby/truffleruby#4308
Since we're handling everything inside the ensure clause, we can drop
the rescue clause and use `$!` in the ensure clause.

But please note that this seems to trigger a TruffleRuby bug:
truffleruby/truffleruby#4308
@nevans nevans changed the title Refactor receive responses thread sync 🧵Refactor receive responses thread sync Jun 8, 2026
@nevans nevans marked this pull request as draft June 8, 2026 14:28
@nevans nevans force-pushed the refactor-receive-responses-thread-sync branch from cf63612 to 3d5f3f3 Compare June 8, 2026 14:41
@nevans

nevans commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator Author

The last bit may make it into a future PR. But the bulk of the refactoring was done via #694.

@nevans nevans closed this Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant